package server;
import java.io.*;
/**
 * This class represents a server-side protocol, in charge of accepting http requests and acting according to the request details
 * the supported request types are GET, PUT and POST
 * @author yariv zur
 */
public class MessageHandler implements ServerProtocol{

	private StringBuilder sb;
	/**
	 * Constructor for MessageHandler class
	 */
	public MessageHandler(){
		sb = new StringBuilder();
	}
	/**
	 * This is the method the protocol uses in order to identify the request type and argument, execute the actions needed for a given request
	 * and return an appropriate response
	 */
	public byte[] processMessage(Request request) {
		byte[] ans = null;

		if (request != null) {
			try {
				String type = request.getType();
				String arg = request.getArgument();
				String server = request.getHost();
				byte[] Response400 = (Globals.m400+"Server: "+server+"\r\n\r\nThe argument supplied is not supported").getBytes("UTF-8");
				if(server == null || server.equals("")){ //the Host header is not sent so the request is invalid
					return Response400;
				}
				if (isEnd(arg)) { //the shutdown argument has been given
					Globals.SHUTDOWN = true;
					return (Globals.m200+"Server: "+request.getHost()+"\r\n\r\nDetails: The server has been shut down").getBytes("UTF-8");
				}
				else{
					if(type.equals("GET")){ //request type is GET
						if(arg.equals("/photos")){ // the request is GET /photos
							sb.append(Globals.xhtmlHeader);
							sb.append(Globals.xmlnsHeader);
							sb.append("<head>\r\n<title>photos</title>\r\n</head>\r\n<body>\r\n<p>Photos</p>\r\n<ol>\r\n");
							synchronized(Globals.resources){
								for(Resource r: Globals.resources){
									sb.append("<li><a href=\""+r.getPath()+"\">Photo "+r.getId()+"</a>\r\n<ol>\r\n");
									for(Representation rep: r.getReps()){
										sb.append("<li><a href=\""+rep.getPath()+"\">Representation "+rep.getId()+"</a></li>\r\n");
									}
									sb.append("</ol>\r\n</li>\r\n");
								}
							}

							sb.append("</ol>\r\n</body>\r\n</html>");
							int length = (sb.toString().getBytes("UTF-8")).length;
							sb.insert(0,Globals.m200+"Server: "+server+"\r\nContent-Type: text/html; charset: utf-8\r\nContent-Length: "+length+"\r\n\r\n");
							return sb.toString().getBytes("UTF-8");
						}
						else if(arg.equals("/jobs")){ // the request is GET /jobs
							sb.append(Globals.xhtmlHeader);
							sb.append(Globals.xmlnsHeader);
							sb.append("<head>\r\n<title>"+server+"/jobs</title>\r\n</head>\r\n<body>\r\n<p>Job Status</p>\r\n<ol>\r\n");
							sb.append("<li>Non Submitted Jobs\r\n<ul>\r\n");
							synchronized(Globals.nonSubmittedJobs){ //go over the nonSubmittedJobs Vector and add a line to the output message for each Job with its details
								for(Job j: Globals.nonSubmittedJobs){
									sb.append("<li><a href=\"jobs/"+j.getJobId()+"\">JOB "+j.getJobId()+"</a>\r\n<a href=\"photos/"+j.getResource()+"\">Resource "+j.getResource()+"</a>.</li>\r\n");
								}
							}
							sb.append("</ul>\r\n</li>\r\n<li>Submitted Jobs\r\n<ul>\r\n");
							synchronized(Globals.submittedJobs){ //go over the submittedJobs Vector and add a line to the output message for each Job with its details
								for(Job j: Globals.submittedJobs){
									sb.append("<li><a href=\"jobs/"+j.getJobId()+"\">JOB "+j.getJobId()+"</a>\r\n<a href=\"photos/"+j.getResource()+"\">Resource "+j.getResource()+"</a>.</li>\r\n");
								}
							}
							sb.append("</ul>\r\n</li>\r\n<li>Finished Jobs\r\n<ul>\r\n");
							synchronized(Globals.finishedJobs){ //go over the finishedJobs Vector and add a line to the output message for each Job with its details
								for(Job j: Globals.finishedJobs){
									sb.append("<li><a href=\"jobs/"+j.getJobId()+"\">JOB "+j.getJobId()+"</a>\r\n<a href=\"photos/"+j.getResource()+"\">Resource "+j.getResource()+"</a>.</li>\r\n");
								}
							}
							sb.append("</ul>\r\n</li>\r\n</ol>\r\n</body>\r\n</html>");
							int length = (sb.toString().getBytes("UTF-8")).length;
							sb.insert(0,Globals.m200+"Server: "+server+"\r\nContent-Type: text/html; charset: utf-8\r\nContent-Length: "+length+"\r\n\r\n");
							return sb.toString().getBytes("UTF-8");
						}
						else if(arg.contains("photos/")){
							if(!(arg.contains("?rep="))){ // the request is GET /photos/[resource id]
								String resourceId = request.getResourceName();
								if(Globals.resources.size() > Integer.parseInt(resourceId)){
									Resource r = Globals.findResource(resourceId);
									sb.append(Globals.xhtmlHeader);
									sb.append(Globals.xmlnsHeader);
									sb.append("<head>\r\n<title>"+server+"/"+r.getPath()+"</title>\r\n</head>\r\n<body>\r\n<p>JOB STATUS CATEGORY</p>\r\n<ol>\r\n");
									for(Representation rep: r.getReps()){ //go over the Resource's representations and add a line to the message for each one
										sb.append("<li><a href=\"?rep="+rep.getId()+"\">Representation "+rep.getId()+"</a>\r\n<ul>\r\n");
										for(Job j: rep.getJobs()){ //go over the Representations' Jobs and add a line to the message for each one
											sb.append("<li><a href=\"../jobs/"+j.getJobId()+"\">JOB "+j.getJobId()+"</a>. Status - "+j.getStatus()+"</li>\r\n");
										}
										sb.append("</ul>\r\n</li>\r\n");
									}
									sb.append("</ol>\r\n</body>\r\n</html>");
									int length = (sb.toString().getBytes("UTF-8")).length;
									sb.insert(0,Globals.m200+"Server: "+server+"\r\nContent-Type: text/html; charset: utf-8\r\nContent-Length: "+length+"\r\n\r\n");
									return sb.toString().getBytes("UTF-8");
								}
								else{ //the requested resource does not exist
									return (Globals.m204+"Server: "+server+"\r\n\r\nThe requested resource could not be found").getBytes("UTF-8");
								}
							}
							else{ // the request is GET /photos/[resource id]?rep=[representation id]
								String resourceId = request.getResourceName();
								String repId = request.getRepresentationName();
								if(Globals.resources.size()-1 >= Integer.parseInt(resourceId)){ // perliminary check that the resource exists
									Resource r = Globals.resources.elementAt(Integer.parseInt(resourceId));
									if(r.getReps().size()-1 >= Integer.parseInt(repId)){ // perliminary check that the representation exists
										Representation rep = r.getReps().elementAt(Integer.parseInt(repId));
										if(!rep.isReady()){ //the representation has been created but the processed image file has not been returned by an employee
											return (Globals.m206+"Server: "+server+"\r\n\r\nThe requested representation is not yet ready").getBytes("UTF-8");
										}
										else{ // load the required representation's file and send it to the client/employee
											String MIME_type = Globals.mt.getType(rep.getFilePath().substring(rep.getFilePath().lastIndexOf('/')+1));
											File file = new File(rep.getFilePath());
											byte[] fileBytes = new byte[(int)file.length()];
											FileInputStream fos = new FileInputStream(file);
											fos.read(fileBytes);
											byte[] lines = (Globals.m200+"Server: "+server+"\r\nContent-Type: "+MIME_type+"\r\n"+"Content-Length: "+fileBytes.length+"\r\n\r\n").getBytes("UTF-8");
											ans = new byte[fileBytes.length+lines.length];
											for(int i=0; i < ans.length; i++){
												ans[i] = i < lines.length ? lines[i] : fileBytes[i-lines.length];
											}
											return ans;
										}
									}
									else{ //the representation does not exist
										return (Globals.m204+"Server: "+server+"\r\n\r\nThe requested representation could not be found").getBytes("UTF-8");
									}
								}
								else{ //the resource does not exist
									return (Globals.m204+"Server: "+server+"\r\n\r\nThe requested resource could not be found").getBytes("UTF-8");
								}
							}
						}
						else if(arg.contains("/jobs/")){ // the request is GET /jobs/[job id]
							String jobId = arg.substring(arg.lastIndexOf('/')+1);
							synchronized(Globals.nonSubmittedJobs){ //go over the nonSubmittedJobs Vector and look for the required job
								for(Job j: Globals.nonSubmittedJobs){
									if(j.getJobId().equals(jobId)){
										return (Globals.m200+"Server: "+server+"\r\nContent-Type: text/html; charset: utf-8\r\nContent-Length: "+(j.getJobBody().getBytes("UTF-8")).length+"\r\n\r\n"+j.getJobBody()).getBytes("UTF-8");
									}
								}
							}
							synchronized(Globals.submittedJobs){ //go over the submittedJobs Vector and look for the required job
								for(Job j: Globals.submittedJobs){
									if(j.getJobId().equals(jobId)){
										return (Globals.m200+"Server: "+server+"\r\nContent-Type: text/html; charset: utf-8\r\nContent-Length: "+(j.getJobBody().getBytes("UTF-8")).length+"\r\n\r\n"+j.getJobBody()).getBytes("UTF-8");
									}
								}
							}
							synchronized(Globals.finishedJobs){ //go over the finishedJobs Vector and look for the required job
								for(Job j: Globals.finishedJobs){
									if(j.getJobId().equals(jobId)){
										return (Globals.m200+"Server: "+server+"\r\nContent-Type: text/html; charset: utf-8\r\nContent-Length: "+(j.getJobBody().getBytes("UTF-8")).length+"\r\n\r\n"+j.getJobBody()).getBytes("UTF-8");
									}
								}
							}
							return (Globals.m404+"Server: "+server+"\r\n\r\nThe requested job could not be found").getBytes("UTF-8"); //the requested job does not exist
						}
						else{ // the request is a GET type but the argument is not one of the supported options
							return Response400;
						}
					}

					else if(type.equals("PUT")){ // this is a PUT request
						if(arg.equals("/upload")){ // the request type is PUT /upload - this is an upload request for a new Resource (from the client)
							Resource newResource = Globals.addResource();
							String resourceDirPath = Globals.PATH+"/resource"+newResource.getId();
							File resourceDir = new File(resourceDirPath);
							if(!resourceDir.exists()){ //if the specific source directory does not exist, create it
								boolean resourceDirCreated = resourceDir.mkdir();
								if(!resourceDirCreated){ // if the directory creation failed do not continue to upload the image
									return (Globals.m500+"Server: "+server+"\r\n\r\nCould not create the folder for the resource"+newResource.getId()).getBytes("UTF-8");
								}
							} //create the new representation 0 , create a file for it and fill it with the given image
							String repFilePath = resourceDirPath+"/0."+request.getContentType().substring(request.getContentType().lastIndexOf('/')+1);
							File repFile = new File(repFilePath);
							FileOutputStream s = new FileOutputStream(repFile);
							s.write(request.getContent());
							Representation newRep = newResource.addRep();
							newRep.setFilePath(repFilePath);
							newRep.setPath("photos/"+newResource.getId()+"?rep=0");
							newRep.setCreatedByJob("upload");
							newRep.setReady();
							int length = (int)(repFile.length());
							sb.append(Globals.m201+"Server: "+server+"\r\nContent-Type: text/html; charset: utf-8\r\nContent-Length: "+length+"\r\n\r\n");
							sb.append(Globals.xhtmlHeader+Globals.xmlnsHeader+"<head>\r\n<title>"+server+'/'+newResource.getPath()+"</title>\r\n</head>\r\n<body>\r\n");
							sb.append("<b>HTTP/1.1 201 Created:</b>\r\n<a href=\""+newResource.getPath()+"\">Resource "+newResource.getId()+"</a>\r\n");
							sb.append("has been created successfully.<br/>\r\nOriginal image can be found <a href=\""+newRep.getPath()+"\">here</a>.\r\n</body>\r\n</html>");
							return sb.toString().getBytes("UTF-8");
						}
						else if(arg.contains("/photos/") && arg.contains("?rep=")){ // the request is PUT /photos/[resource id]?rep=[representation id] - this is an upload request for a new Representation (from an employee)
							String resourceId = request.getResourceName();
							String representationId = request.getRepresentationName();
							Resource res = Globals.findResource(resourceId);
							Representation rep = res.getRepresentation(representationId);
							if(rep == null){ //the requested representation does not exist
								return (Globals.m500+"Server: "+server+"\r\n\r\nCould not find the Representation for the given image").getBytes("UTF-8");
							}
							else{ //load the uploaded image from the employee and save it to a file in the file system and update the representation accordingly
								Globals.moveToFinished(rep.getCreatedByJob());
								File representationFile = new File(rep.getFilePath());
								FileOutputStream s = new FileOutputStream(representationFile);
								s.write(request.getContent()); //write the file content
								rep.setReady();
								int length = (int)(representationFile.length());
								sb.append(Globals.m200+"Server: "+server+"\r\nContent-Type: text/html; charset: utf-8\r\nContent-Length: "+length+"\r\n\r\n");
								sb.append(Globals.xhtmlHeader+Globals.xmlnsHeader);
								sb.append("<head>\r\n<title>"+server+'/'+rep.getPath()+"</title>\r\n</head>\r\n<body>\r\n");
								sb.append("<b>HTTP/1.1 200 OK:</b>\r\n<a href="+rep.getPath()+">Representation "+rep.getId()+"</a>\r\n");
								sb.append("has been created successfully.<br/>\r\n</body>\r\n</html>");
								return sb.toString().getBytes("UTF-8");
							}
						}
						else{ // the request is a PUT type but the argument is not one of the supported options
							return Response400;
						}
					}
					else if(type.equals("POST")){ //this is a POST request type
						if(arg.contains("/photos/")){ //this requet is POST /photos/[resource id] - uploading a job from an XML file
							String resourceName = request.getResourceName();
							String doc = new String(request.getContent(), "UTF-8");
							Job j = Globals.addNewJob(); //create a new job and add it to the nonSubmittedJobs vector
							j.setJobBody(doc);
							j.setJobResource(resourceName);
							j.setStatus("not submitted");
							Resource res = Globals.findResource(resourceName);
							Representation out = res.addRep();
							out.setCreatedByJob(j.getJobId());
							Representation rep0 = res.getReps().elementAt(0);
							String file_type = (rep0.getFilePath()).substring(rep0.getFilePath().lastIndexOf(".")+1); //determine the file type from the original image
							out.setFilePath(Globals.PATH+"/resource"+res.getId()+"/"+out.getId()+"."+file_type);
							out.setPath(res.getPath()+"?rep="+out.getId());
							j.setOutputRepresentation(out.getId());
							String inputRepId = j.getInputRepresentation();
							Representation inputRep = res.getRepresentation(inputRepId);
							if(inputRep == null){ // a job has been given to use an InputRepresentation that does not exist yet
								return Response400;
							}
							else if(inputRep.isReady()){ //if the InputRepresentation exists, add this job to its jobs Vector
								j.setReady();
							}
							inputRep.getJobs().addElement(j);
							sb.append(Globals.xmlnsHeader+"<head>\r\n<title>"+server+"/jobs/"+j.getJobId()+"</title>\r\n</head>\r\n<body>\r\n"); //RETURN 202 with xml body  with a link to the output representation in the body like example1 in PDF
							sb.append("<b>HTTP/1.1 202 Accepted:</b>\r\n<a href=\"jobs/"+j.getJobId()+"\">Job "+j.getJobId()+"</a> ");
							sb.append("has been accepted successfully.<br/>\r\nResult image can be found <a href=\""+out.getPath()+"\">here</a>.\r\n</body>\r\n</html>");
							int length = (sb.toString().getBytes("UTF-8")).length;
							sb.insert(0,Globals.m202+"Server: "+server+"\r\nContent-Type: text/html; charset: utf-8\r\nContent-Length: "+length+"\r\n\r\n");
							return sb.toString().getBytes("UTF-8");
						}
						else if(arg.equals("/jobs/get-new-job")){ //the request is POST /jobs/get-new-job
							Job j = Globals.getNewJob(); //get the next job waiting in the nonSubmittedJobs Vector
							if(j == null){ //if no jobs are waiting to be submitted, return a 204 No Content response
								return (Globals.m204+"Server: "+server+"\r\n\r\nThere are no available jobs at the moment").getBytes("UTF-8");
							}
							else if(!j.isReady()){ //if the job's InputRepresentation was not ready when the job was uploaded
								Resource res = Globals.findResource(j.getResource()); // see if the representation has been created by now and update the job accordingly
								String inputRepId = j.getInputRepresentation();
								Representation rep = res.getRepresentation(inputRepId);
								if(!rep.isReady()){ // if the InputRepresentation is still not created, return a 204 No Content response
									synchronized(Globals.nonSubmittedJobs){
										j.setStatus("not submitted");
										Globals.nonSubmittedJobs.addElement(j);
									}
									return (Globals.m204+"Server: "+server+"\r\n\r\nThere are no available jobs at the moment").getBytes("UTF-8");
								}
							}
							else{ //return a 200 Ok response with the Job in XML syntax in the message body
								String body = j.getJobBody();
								return (Globals.m200+"Server: "+server+"\r\nresource: "+j.getResource()+"\r\nContent-Type: text/html; charset: utf-8\r\nContent-Length: "+(body.getBytes("UTF-8").length)+"\r\n\r\n"+body).getBytes("UTF-8");
							}
						}
						else{ // the request is a POST type but the argument is not one of the supported options
							return Response400;
						}
					}
					else{ //the request type is not supported
						return Response400;
					}
				}
			}
			catch (IOException e) {
				try { //if a server error has occured return a 500 response
					System.out.println("An error has occured in the server: "+e.getMessage());
					return (Globals.m500+"Server: "+request.getHost()+"\r\n\r\nan error has occured in the server").getBytes("UTF-8");
				} catch (UnsupportedEncodingException e1) {}
			}
		}
		return ans;
	}
	/**
	 * This method checks the request and returns true if it is a shutdown command, false otherwise
	 */
	public boolean isEnd(String msg) {
		return msg.equals("/shutdown");
	}

}

